package com.jprocms.module.cms.service.investigate;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.jpro.framework.common.pojo.PageResult;
import com.jpro.framework.common.util.collection.CollectionUtils;
import com.jpro.framework.common.util.object.BeanUtils;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.pagehelper.page.PageMethod;
import com.jprocms.module.cms.controller.admin.investigate.vo.InvestigateCreateReqVO;
import com.jprocms.module.cms.controller.admin.investigate.vo.InvestigateListReqVO;
import com.jprocms.module.cms.controller.admin.investigate.vo.InvestigatePageReqVO;
import com.jprocms.module.cms.controller.admin.investigate.vo.InvestigateUpdateReqVO;
import com.jprocms.module.cms.controller.admin.investigateitem.vo.InvestigateItemCreateReqVO;
import com.jprocms.module.cms.controller.admin.investigateitem.vo.InvestigateItemUpdateReqVO;
import com.jprocms.module.cms.controller.admin.investigateitemoption.vo.InvestigateItemOptionCreateReqVO;
import com.jprocms.module.cms.convert.PageConverter;
import com.jprocms.module.cms.convert.investigate.InvestigateConvert;
import com.jprocms.module.cms.convert.investigateitem.InvestigateItemConvert;
import com.jprocms.module.cms.convert.investigateitemoption.InvestigateItemOptionConvert;
import com.jprocms.module.cms.dal.dataobject.investigate.InvestigateDO;
import com.jprocms.module.cms.dal.dataobject.investigateitem.InvestigateItemDO;
import com.jprocms.module.cms.dal.dataobject.investigateitemop.InvestigateItemOpDO;
import com.jprocms.module.cms.dal.dataobject.investigateitemoption.InvestigateItemOptionDO;
import com.jprocms.module.cms.dal.dataobject.investigateopinion.InvestigateOpinionDO;
import com.jprocms.module.cms.dal.dataobject.investigateoptionop.InvestigateOptionOpDO;
import com.jprocms.module.cms.dal.mysql.investigate.InvestigateMapper;
import com.jprocms.module.cms.dal.mysql.investigateitem.InvestigateItemMapper;
import com.jprocms.module.cms.dal.mysql.investigateitemop.InvestigateItemOpMapper;
import com.jprocms.module.cms.dal.mysql.investigateitemoption.InvestigateItemOptionMapper;
import com.jprocms.module.cms.dal.mysql.investigateopinion.InvestigateOpinionMapper;
import com.jprocms.module.cms.dal.mysql.investigateoptionop.InvestigateOptionOpMapper;
import com.jprocms.module.cms.framework.seq.SnowflakeSequence;
import com.mzt.logapi.context.LogRecordContext;
import com.mzt.logapi.service.impl.DiffParseFunction;
import com.mzt.logapi.starter.annotation.LogRecord;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.jpro.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.jprocms.module.cms.enums.ErrorCodeConstants.INVESTIGATE_NOT_EXISTS;
import static com.jprocms.module.cms.enums.LogRecordConstants.*;

/**
 * 问卷调查 Service 实现类
 *
 * @author jprocms
 */
@Service
@Validated
public class InvestigateServiceImpl implements InvestigateService {

    @Resource
    private InvestigateMapper investigateMapper;
    @Resource
    private InvestigateOpinionService opinionService;

    @Resource
    private InvestigateItemMapper investigateItemMapper;
    @Resource
    private InvestigateItemOpMapper investigateItemOpMapper;

    @Resource
    private InvestigateItemOptionMapper investigateItemOptionMapper;
    @Resource
    private InvestigateOptionOpMapper investigateOptionOpMapper;
    @Resource
    private InvestigateOpinionMapper opinionMapper;
    @Resource
    private PageConverter pageConverter;
    @Resource
    private SnowflakeSequence snowflakeSequence;

    @Transactional(rollbackFor = Exception.class)
    public void vote(Long id, Map<Long, List<Long>> optionMap, Map<Long, String> itemAnsMap,
                     Long siteId, @Nullable Long userId, String ip, Long cookie) {
        investigateMapper.vote(id);
        InvestigateDO investigateDO = Optional.ofNullable(investigateMapper.select(id)).orElseThrow(() ->
                new IllegalStateException("调查 ID不存在: " + id));
        InvestigateOpinionDO opinionDO = new InvestigateOpinionDO(investigateDO.getId(), siteId, userId, ip, cookie.toString());
        opinionMapper.insert(opinionDO);

        for (Map.Entry<Long, List<Long>> option : optionMap.entrySet()) {
            Long itemId = option.getKey();
            investigateItemOptionMapper.vote(itemId, option.getValue());
            option.getValue().forEach(optionId -> opinionService.insertOption(
                    new InvestigateOptionOpDO(optionId, opinionDO.getId(), investigateDO.getId(), itemId)));
        }
        for (Map.Entry<Long, String> itemAns : itemAnsMap.entrySet()) {
            opinionService.insertItem(new InvestigateItemOpDO(itemAns.getKey(), opinionDO.getId(),
                    investigateDO.getId(), itemAns.getValue()));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @LogRecord(type = CMS_INVESTIGATE_TYPE, subType = CMS_INVESTIGATE_CREATE_SUB_TYPE, bizNo = "{{#investigate.id}}", success = CMS_INVESTIGATE_CREATE_SUCCESS)
    public Long createInvestigate(InvestigateCreateReqVO createReqVO) {
        // 插入
        InvestigateDO investigate = InvestigateConvert.INSTANCE.convertMore(createReqVO);
        investigateMapper.insert(investigate);
        List<InvestigateItemCreateReqVO> items = createReqVO.getItems();
        int order = 1;
        for (InvestigateItemCreateReqVO item : items) {
            //插入题目
            item.setItemSort(order);
            item.setSiteId(createReqVO.getSiteId());
            order += 1;
            item.setInvestigateId(investigate.getId());
            InvestigateItemDO itemDO = InvestigateItemConvert.INSTANCE.convert(item);
            itemDO.setId(snowflakeSequence.nextId());
            investigateItemMapper.insert(itemDO);

            // 插入选项
            int optionOrder = 1;
            List<InvestigateItemOptionCreateReqVO> options = item.getOptions();
            for (InvestigateItemOptionCreateReqVO option : options) {
                option.setInvestigateItemId(itemDO.getId());
                option.setInvestigateId(investigate.getId());
                option.setSiteId(createReqVO.getSiteId());
                InvestigateItemOptionDO itemOptionDO = InvestigateItemOptionConvert.INSTANCE.convert(option);
                itemOptionDO.setOptionSort(optionOrder);
                optionOrder += 1;
                itemOptionDO.setId(snowflakeSequence.nextId());
                investigateItemOptionMapper.insert(itemOptionDO);
            }
        }
        // 记录操作日志上下文
        LogRecordContext.putVariable("investigate", investigate);
        // 返回
        return investigate.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @LogRecord(type = CMS_INVESTIGATE_TYPE, subType = CMS_INVESTIGATE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", success = CMS_INVESTIGATE_UPDATE_SUCCESS)
    public void updateInvestigate(InvestigateUpdateReqVO updateReqVO) {
        // 校验存在
        InvestigateDO oldInvestigate = validateInvestigateExists(updateReqVO.getId());
        // 更新
        InvestigateDO updateObj = InvestigateConvert.INSTANCE.convertMore(updateReqVO);
        investigateMapper.updateById(updateObj);
        final InvestigateDO investigateDO = investigateMapper.select(updateObj.getId());

        List<InvestigateItemUpdateReqVO> items = updateReqVO.getItems();
        int order = 1;
        for (InvestigateItemUpdateReqVO item : items) {
            item.setItemSort(order);
            order += 1;
            updateObj.setSortNum(order);
            item.setInvestigateId(updateObj.getId());
        }
        List<InvestigateItemDO> voteItemDOS = InvestigateItemConvert.INSTANCE.convertListForUpdate(items);
        List<InvestigateItemDO> toUpdateItems = voteItemDOS.stream().filter(item -> item.getId() > 0).collect(Collectors.toList());
        List<InvestigateItemDO> toInsertItems = voteItemDOS.stream().filter(item -> item.getId() <= 0).collect(Collectors.toList());
        final List<Long> toSaveOrUpdateIds = CollectionUtils.convertList(voteItemDOS, InvestigateItemDO::getId);
        final List<InvestigateItemDO> dbItems = investigateDO.getItems();
        final List<Long> dbItemIds = CollectionUtils.convertList(dbItems, InvestigateItemDO::getId);
        final List<Long> toDelItemIds = CollUtil.subtractToList(dbItemIds, toSaveOrUpdateIds);
        //删除题目的选项的结果
        //删除题目的选项
        for (Long toDelItemId : toDelItemIds) {
            investigateOptionOpMapper.deleteByItemId(toDelItemId, null);
            investigateItemOptionMapper.deleteByItemId(toDelItemId, null);
        }
        if (CollUtil.isNotEmpty(toDelItemIds)) {
            //删除题目的结果
            investigateItemOpMapper.deleteByItemIds(toDelItemIds);
            //删除题目
            investigateItemMapper.deleteBatchIds(toDelItemIds);
        }

        //插入新题目
        for (InvestigateItemDO item : toInsertItems) {
            //插入题目
            item.setSiteId(updateReqVO.getSiteId());
            item.setInvestigateId(updateObj.getId());
            item.setId(snowflakeSequence.nextId());
            investigateItemMapper.insert(item);

            // 插入选项
            int optionOrder = 1;
            List<InvestigateItemOptionDO> options = item.getOptions();

            for (InvestigateItemOptionDO option : options) {
                option.setInvestigateItemId(item.getId());
                option.setInvestigateId(updateObj.getId());
                option.setSiteId(updateObj.getSiteId());
                option.setOptionSort(optionOrder);
                optionOrder += 1;
                option.setOptionSort(optionOrder);
                option.setId(snowflakeSequence.nextId());
                investigateItemOptionMapper.insert(option);
            }
        }

        for (InvestigateItemDO toUpdateItem : toUpdateItems) {
            investigateItemMapper.updateById(toUpdateItem);
            List<InvestigateItemOptionDO> options = toUpdateItem.getOptions();
            List<InvestigateItemOptionDO> toUpdateOptions = options.stream().filter(item -> item.getId() > 0).collect(Collectors.toList());
            List<InvestigateItemOptionDO> toInsertOptions = options.stream().filter(item -> item.getId() <= 0).collect(Collectors.toList());
            final InvestigateItemDO dbItemDO = investigateItemMapper.select(toUpdateItem.getId());
            final List<InvestigateItemOptionDO> dbOptionDOS = dbItemDO.getOptions();
            final List<Long> dbOptionIds = CollectionUtils.convertList(dbOptionDOS, InvestigateItemOptionDO::getId);

            final List<Long> toSaveOrUpdateOptionIds = CollectionUtils.convertList(options, InvestigateItemOptionDO::getId);
            final List<Long> toDelOptionIds = CollUtil.subtractToList(dbOptionIds, toSaveOrUpdateOptionIds);
            //删除题目的选项的结果
            //删除题目的选项
            if (CollUtil.isNotEmpty(toDelOptionIds)) {
                investigateOptionOpMapper.deleteByOptionIds(toDelOptionIds);
                investigateItemOptionMapper.deleteBatchIds(toDelOptionIds);
            }

            //新增的选项
            int optionOrder = 1;
            for (InvestigateItemOptionDO option : toInsertOptions) {
                option.setInvestigateItemId(toUpdateItem.getId());
                option.setInvestigateId(updateObj.getId());
                option.setSiteId(updateObj.getSiteId());
                option.setOptionSort(optionOrder);
                optionOrder += 1;
                option.setOptionSort(optionOrder);
                option.setId(snowflakeSequence.nextId());
                investigateItemOptionMapper.insert(option);
            }

            //修改选项
            for (InvestigateItemOptionDO toUpdateOption : toUpdateOptions) {
                optionOrder += 1;
                toUpdateOption.setOptionSort(optionOrder);
                investigateItemOptionMapper.updateById(toUpdateOption);
            }
        }
        // 记录操作日志上下文
        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldInvestigate, InvestigateUpdateReqVO.class));
        LogRecordContext.putVariable("investigate", updateObj);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @LogRecord(type = CMS_INVESTIGATE_TYPE, subType = CMS_INVESTIGATE_DELETE_SUB_TYPE, bizNo = "{{#id}}", success = CMS_INVESTIGATE_DELETE_SUCCESS)
    public void deleteInvestigate(Long id) {
        // 校验存在
        InvestigateDO investigate = validateInvestigateExists(id);
        //删除选项和选项意见反馈
        investigateItemOptionMapper.deleteByInvestigateId(id, null);
        investigateOptionOpMapper.deleteByInvestigateId(id, null);
        //删除题目和题目意见反馈
        investigateItemOpMapper.deleteByInvestigateId(id, null);
        investigateItemMapper.deleteByInvestigateId(id);
        //删除调查意见反馈
        opinionMapper.deleteByInvestigateId(id);
        //删除
        investigateMapper.deleteById(id);
        // 记录操作日志上下文
        LogRecordContext.putVariable("investigate", investigate);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteByIds(List<Long> ids) {
        if (!org.springframework.util.CollectionUtils.isEmpty(ids)) {
            for (Long id : ids) {
                deleteInvestigate(id);
            }
        }
    }

    private InvestigateDO validateInvestigateExists(Long id) {
        final InvestigateDO investigateDO = investigateMapper.selectById(id);
        if (investigateDO == null) {
            throw exception(INVESTIGATE_NOT_EXISTS);
        }
        return investigateDO;
    }

    @Override
    public InvestigateDO getInvestigate(Long id) {
        return investigateMapper.select(id);
    }

    @Override
    public List<InvestigateDO> getInvestigateList(Collection<Long> ids) {
        return investigateMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<InvestigateDO> getInvestigatePage(InvestigatePageReqVO pageReqVO) {
        if (pageReqVO.getBeginDate() != null) {
            if (pageReqVO.getBeginDate().length > 0 && pageReqVO.getBeginDate()[0] != null) {
                pageReqVO.setBeginDateStart(pageReqVO.getBeginDate()[0]);
            }
            if (pageReqVO.getBeginDate().length > 1 && pageReqVO.getBeginDate()[1] != null) {
                pageReqVO.setBeginDateEnd(pageReqVO.getBeginDate()[1]);
            }
        }
        pageReqVO.setNow(Date.from(DateUtil.date().toInstant()));
        IPage<InvestigateDO> voteDOIPage = investigateMapper.selectPage(pageReqVO, pageReqVO.getOrderItems(), new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()));
        return pageConverter.page(voteDOIPage);
    }

    @Override
    public List<InvestigateDO> getInvestigateList(InvestigateListReqVO exportReqVO) {
        exportReqVO.setNow(Date.from(DateUtil.date().toInstant()));
        return PageMethod.offsetPage(exportReqVO.getOffset(), exportReqVO.getCount(), false).doSelectPage(() -> investigateMapper.selectList(exportReqVO, exportReqVO.getOrderItems()));
    }

}
